package eu.ict_omelette.mashup.adapters.pipes;

import eu.ict_omelette.mashup.adapters.pipes.database.DatabaseParser;
import eu.ict_omelette.mashup.canonicalmodel.CanonicalMashup;
import eu.ict_omelette.mashup.canonicalmodel.Component;
import eu.ict_omelette.mashup.canonicalmodel.Dataflow;
import eu.ict_omelette.mashup.canonicalmodel.GlobalParameter;
import eu.ict_omelette.mashup.canonicalmodel.InputParameter;
import eu.ict_omelette.mashup.transformer.api.AdapterPlugin;
import java.io.File;
import java.net.MalformedURLException;
import java.net.URI;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import javax.annotation.processing.FilerException;

/**
 *
 * @author guybrush
 */
public class Canonical2Json implements AdapterPlugin {

    static final String EMBED = "embed";

    KindOfFile kof = null;
    CanonicalMashup cm = null;

    public KindOfFile getKindOfFile() { return kof; }
    public void setKindOfFile(KindOfFile kof) { this.kof = kof; }

    public Canonical2Json(KindOfFile kof) { this.kof = kof; }
    public Canonical2Json(KindOfFile kof, URI uri) throws MalformedURLException
    { this.kof = kof; loadSourceMashupModel(uri); }
    public Canonical2Json(KindOfFile kof, String filepath) throws MalformedURLException
    { this.kof = kof; loadSourceMashupModel(filepath); }

    final public void loadSourceMashupModel(String filepath) throws MalformedURLException
    { loadSourceMashupModel(new File(filepath).toURI()); }
    final public void loadSourceMashupModel(URI uri) throws MalformedURLException {

        String tmp[] = uri.getPath().split("/"), filename = tmp[tmp.length -1];
        if (filename != null /*&&
           ((kof.equals(KindOfFile.json) && filename.startsWith("pipe")) ||
            (kof.equals(KindOfFile.serialized) && filename.endsWith(".ser")))*/)
            switch(kof)
            {
                case json: PipesAdapter adapter = new PipesAdapter(); adapter.loadSourceMashupModel(uri);
                           cm = adapter.getCanonicalMashup(); break;
                case serialized: cm = CanonicalMashup.unserialize(uri.getPath()); break;
            }
        else throw new MalformedURLException("Path: " + uri.getPath() + "\nNot recognizable file.");

    }
    public CanonicalMashup getCanonicalMashup() { return cm; }
    public List<Component> getComponents()
    {
        if (cm == null) return null;
        List<Component> comps = new ArrayList<Component>();
        Iterator<Component> it = cm.getComponents();
        while(it.hasNext()) comps.add(it.next());

        if (comps.size() > 0) return comps;
        return null;
    }
    public List<Dataflow> getDataflows()
    {
        if (cm == null) return null;
        List<Dataflow> flows = new ArrayList<Dataflow>();
        Iterator<Dataflow> it = cm.getDataFlows();
        while(it.hasNext()) flows.add(it.next());

        if (flows.size() > 0) return flows;
        return null;
    }
    public List<GlobalParameter> getGlobalParameters()
    {
        if (cm == null) return null;
        List<GlobalParameter> params = new ArrayList<GlobalParameter>();
        Iterator<GlobalParameter> it = cm.getGlobalParameters();
        while(it.hasNext()) params.add(it.next());

        if (params.size() > 0) return params;
        return null;
    }

    public String converteCanonicalMashup() throws SQLException { if (cm == null) return null; return converteCanonicalMashup(cm); }
    static public String converteCanonicalMashup(CanonicalMashup cm) throws SQLException
    {
        return "{\"layout\":[]," + converteComponents(cm.getComponents())
                + ",\"terminaldata\":[]," + converteDataFlows(cm.getDataFlows()) + "}";
    }
    public String converteDataFlows() { if (cm == null) return null; return converteDataFlows(cm.getDataFlows()); }
    static public String converteDataFlows(Iterator<Dataflow> it) { return converteDataFlows(it, true); }
    static public String converteDataFlows(Iterator<Dataflow> it, Boolean typeModul)
    {
        /* Body expected:
         *
         * "wires":[{"id":"_w","src":{"id":"","moduleid":"srcType"},"tgt":{"id":"","moduleid":"tgtType"}},
         *          {"id":"_w","src":{"id":"","moduleid":"srcType"},"tgt":{"id":"","moduleid":"tgtType"}}]
         */

        Integer index = 1;
        String ret = "\"wires\":[";

        if (it.hasNext())
            while(true) {
                Dataflow df = it.next();
                String srcMod = df.getFromComponent().getType(),
                       tgtMod = df.getToComponent().getType();

                if (!typeModul)
                { srcMod = df.getFromComponent().getId(); tgtMod = df.getToComponent().getId(); }

                ret+="{\"id\":\"_w" + (index) + "\","
                        + "\"src\":{\"id\":\"" + df.getFromPort().getName() + "\",\"moduleid\":\"" + srcMod + "\"},"
                        + "\"tgt\":{\"id\":\"" + df.getToPort().getName() + "\",\"moduleid\":\"" + tgtMod + "\"}}";
                index += 2;
                
                if (it.hasNext()) ret += ","; else break;
            }

        return (ret + "]").replaceAll("\\\\", "\\\\\\\\");
    }
    public String converteComponents() throws SQLException { if (cm == null) return null; return converteComponents(cm.getComponents()); }
    static public String converteComponents(Iterator<Component> it) throws SQLException
    {
        /* Body expected:
         *
         * "modules":[{"type":"","id":"","conf":{}},{"type":"","id":"","conf":{}}]
         */

        String ret = "\"modules\":[";

        if (it.hasNext())
            while(true) {
                ret += convertComponent(it.next());
                if (it.hasNext()) ret += ","; else break;
            }

        return (ret + "]").replaceAll("\\\\", "\\\\\\\\");
    }
    static private String convertComponent(Component c) throws SQLException
    {
        return "{\"type\":\"" + c.getType() + "\",\"id\":\"" + c.getId() + "\",\"conf\":{"
             + converteInputParameters(c.getType(), c.getInputParameters(), c.getEmbeddedComponents()) + "}}";
    }
    static public String converteInputParameters(String type, Iterator<InputParameter> ip, Iterator<Component> ic) throws SQLException
    {
        /* Body expected:
         *
         * no _ or . in name ->  "":{"value":"","type":""},"":{"value":"","type":""}
         * pref_1,pref_2,... -> "pref":[{"value":"","type":""},{"value":"","type":""},...]
         * pref.post1,pref.post2,... -> "pref":{"post1":{"value":"","type":""},"post2":{"value":"","type":""},...}
         * pref_0.post1,pref_0.post2,pref_1.post3,pref_1.post4,... -> "pref":[{"post1":{"value":"","type":""},"post2":{"value":"","type":""}},
	 *  							   	      {"post3":{"value":"","type":""},"post4":{"value":"","type":""}}]
	 *
         */

        ArrayList<String> simple = new ArrayList<String>(); //[pair] = name of input; [odd] = value of input;
        ArrayList<ArrayList<String>> cmplx_underscored = new ArrayList<ArrayList<String>>(); //[0] = name of list; [>0] = value of input;
        ArrayList<ArrayList<String>> cmplx_pointed = new ArrayList<ArrayList<String>>(); //[0] = name of list; [odd index] = name of input; [pair index] = value of input;
        ArrayList<ArrayList<ArrayList<String>>> cmplx_both = new ArrayList<ArrayList<ArrayList<String>>>(); //[0][0] = name of list; [>0][pair index] = name of input; [>0][odd index] = value of input;

        
        getConfLists(ip, simple, cmplx_underscored, cmplx_pointed, cmplx_both);
        mergeWhitTemplate(type, simple, cmplx_underscored, cmplx_pointed, cmplx_both);

        //Boolean well_embedded = false;
        //String embd = "";
        //if (ic != null && (well_embedded = ic.hasNext()))
        //embd = "\"" + EMBED + "\":{\"value\":" + convertComponent(ic.next()) + ",\"type\":\"module\"}";

        String ret = getStringToLists(simple, cmplx_underscored, cmplx_pointed, cmplx_both, false);//well_embedded);
        //if (ret != null && !ret.isEmpty() && !ret.equals("") && embd != null && embd.length() > 0) ret = "," + ret;
        //temporary remove of embedded components
        //ret = embd + ret;

        return ret;
    }
    static public String getStringToLists(ArrayList<String> simple,
                                        ArrayList<ArrayList<String>> cmplx_underscored,
                                        ArrayList<ArrayList<String>> cmplx_pointed,
                                        ArrayList<ArrayList<ArrayList<String>>> cmplx_both,
                                        Boolean skip_embd)
    {
        String ret = "";
        Boolean need_comma = false;
        if (simple.size() > 0)
        {
                for(Iterator<String> it = simple.iterator(); true;)
            {
                need_comma = true;
                String val = it.next();
                if (skip_embd && val.equals(EMBED)) { it.next(); if (ret.endsWith(",")) ret = ret.substring(0, ret.length() - 1); }
                else ret += "\"" + val + "\":" + it.next();
                if (it.hasNext()) ret += ","; else break;
            }
        }
        if (cmplx_underscored.size() > 0)
        {
            if (need_comma) ret += ","; need_comma = true;
            for (Iterator<ArrayList<String>> it = cmplx_underscored.iterator(); true;) {
                ArrayList<String> param = it.next();
                Integer sz = param.size();

                ret += "\"" + param.get(0) + "\":[";
                for (int x=1;x<sz-1;x++)
                    ret += param.get(x) + ",";
                if (sz > 1) ret += param.get(sz-1);
                ret += "]";

                if (it.hasNext()) ret += ","; else break;
            }
        }
        if (cmplx_pointed.size() > 0)
        {
            if (need_comma) ret += ","; need_comma = true;
            for (Iterator<ArrayList<String>> it = cmplx_pointed.iterator(); true;) {
                ArrayList<String> param = it.next();
                Integer sz = param.size();

                ret += "\"" + param.get(0) + "\":{";
                for (int x=1;x<sz-2;x=x+2)
                    ret += "\"" + param.get(x) + "\":" + param.get(x+1) + ",";
                ret += "\"" + param.get(sz-2) + "\":" + param.get(sz-1) + "}";


                if (it.hasNext()) ret += ","; else break;
            }
        }
        if (cmplx_both.size() > 0)
        {
            if (need_comma) ret += ",";
            for (Iterator<ArrayList<ArrayList<String>>> it = cmplx_both.iterator(); true;) {
                ArrayList<ArrayList<String>> params = it.next();
                Integer sz = params.size();

                ret += "\"" + params.get(0).get(0) + "\":[";
                for (int x=1;x<sz-1;x++)
                    ret += converteSubSet(params.get(x)) + ",";
                ret += converteSubSet(params.get(sz-1)) + "]";

                if (it.hasNext()) ret += ","; else break;
            }
        }

        return ret;
    }
    static public void getConfLists(Iterator<InputParameter> ip, ArrayList<String> simple,
                                                                 ArrayList<ArrayList<String>> cmplx_underscored,
                                                                 ArrayList<ArrayList<String>> cmplx_pointed,
                                                                 ArrayList<ArrayList<ArrayList<String>>> cmplx_both)
    {
        while(ip.hasNext()){
            InputParameter p = ip.next(); String p_name = p.getName();

            if (!(isComplexUnderscored(p_name) || p_name.contains(".")))
            { simple.add(p_name); simple.add(converteValueInputParameter(p)); }
            else
                if (p_name.contains("_") && p_name.contains("."))
                {
                    Boolean finded = false;
                    String root = p_name.split("_")[0], name = p_name.split("\\.")[1],
                           tmpInd = p_name.split("_")[1].split("\\.")[0];
                    Integer index = -1;

                    try
                    { index = Integer.parseInt(tmpInd) + 1; if (index.equals(-1)) throw new RuntimeException(); }
                    catch(RuntimeException ex)
                    { ex.printStackTrace(); }

                    for(ArrayList<ArrayList<String>> set : cmplx_both)
                    {
                        ArrayList<String> list = set.get(0);
                        if (list.get(0).equals(root))
                        {
                            if (set.size() > index)
                            {
                                set.get(index).add(name);
                                set.get(index).add(converteValueInputParameter(p));
                            }
                            else
                            {
                                ArrayList<String> tmp = new ArrayList<String>();
                                tmp.add(name); tmp.add(converteValueInputParameter(p));
                                set.add(tmp);
                            }

                            finded = true; break;
                        }
                    }
                    if (!finded)
                    {
                        ArrayList<ArrayList<String>> newList = new ArrayList<ArrayList<String>>();
                        ArrayList<String> tmpName = new ArrayList<String>(); tmpName.add(root); newList.add(tmpName);
                        ArrayList<String> tmpVal = new ArrayList<String>(); tmpVal.add(name);
                        tmpVal.add(converteValueInputParameter(p)); newList.add(tmpVal);
                        cmplx_both.add(newList);
                    }
                }
                else
                {
                if (p_name.contains("_"))
                {
                    Boolean finded = false;
                    String root = p_name.split("_")[0];
                    for (ArrayList<String> v : cmplx_underscored)
                        if (v.get(0).equals(root))
                        {
                            finded = true; String val = converteValueInputParameter(p);
                            if (!v.contains(val)) v.add(val);
                            break;
                        }
                    if (!finded)
                    {
                        ArrayList<String> tmp = new ArrayList<String>();
                        tmp.add(root); tmp.add(converteValueInputParameter(p));
                        cmplx_underscored.add(tmp);
                    }
                }
                if (p_name.contains("."))
                {
                    Boolean finded = false;
                    String[] sub_name = p_name.split("\\.");
                    String root = sub_name[0];
                    for (ArrayList<String> v : cmplx_pointed)
                        if (v.get(0).equals(root)) { v.add(sub_name[1]); v.add(converteValueInputParameter(p)); finded = true; break; }
                    if(!finded)
                    {
                        ArrayList<String> tmp = new ArrayList<String>();
                        tmp.add(root); tmp.add(sub_name[1]); tmp.add(converteValueInputParameter(p));
                        cmplx_pointed.add(tmp);
                    }
                }
            }
        }
    }
    static public void mergeWhitTemplate(String type, ArrayList<String> simple,
                                                      ArrayList<ArrayList<String>> cmplx_underscored,
                                                      ArrayList<ArrayList<String>> cmplx_pointed,
                                                      ArrayList<ArrayList<ArrayList<String>>> cmplx_both) throws SQLException
    { DatabaseParser.getTemplatedLists(type, simple, cmplx_underscored, cmplx_pointed, cmplx_both); }
    static private String converteValueInputParameter(InputParameter param)
    {
        final String term = "terminal";
        String val = param.getValue(), typ = param.getType();
        //debug:
            if (val == null || val.contains("\"")) val = ""; if (typ == null || typ.contains("\"")) typ = "";
        //

        if (val.contains(term + ":")) return "{\"type\":\"" + typ + "\",\"" + term + "\":\"" + val.split(":")[1] + "\"}";
        return "{\"value\":\"" + val + "\",\"type\":\"" + typ + "\"}";
    }
    static private String converteSubSet(ArrayList<String> subset)
    {
        String ret = "{";

        Integer sz = subset.size();
        for (int y=0;y<sz-2;y=y+2)
        {
            ret += "\"" + subset.get(y) + "\":" + subset.get(y+1) + ",";
        }
        ret +="\"" + subset.get(sz-2) + "\":" + subset.get(sz-1) + "}";

        return ret;
    }
    static public String getConfOfComp(Component c) throws SQLException
    { return "{\"conf\":{" + converteInputParameters(c.getType(), c.getInputParameters(), c.getEmbeddedComponents()) + "}}"; }
    static private Boolean isComplexUnderscored(String val)
    {
        if (!val.contains("_")) return false;                   //notice some name's field contain '_'
                                                                //so we need to disambiguate 'URL_0' from 'private_value'
        final String numbers = "01234567489";                   //hoping Yahoo never introduce simple name's filed containing
                                                                //'_' char and end whit *_[0-9]+ (underscore followed by digits
        String splittedVal[] = val.split("_"),
               index = splittedVal[splittedVal.length - 1];

        for (int x=0;x<index.length();x++) if (!numbers.contains(String.valueOf(index.charAt(x)))) return false;

        return true;
    }
}






